/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git.http;

import java.io.IOException;
import java.io.OutputStream;

import org.zhiqim.git.GitConstants;

import org.zhiqim.kernel.constants.EnumConstants;
import org.zhiqim.kernel.util.Asserts;

/**
 * 边频带输出流（多路复用数据和和进度消息）
 *
 * @version v1.0.0 @author zouzhigang 2017-4-19 新建与整理
 */
class SideBandOutputStream extends OutputStream implements EnumConstants, GitConstants
{
    private final OutputStream out;
    private final byte[] buffer;
    private int cnt;

    /**
     * 创建边频带输出流
     * 
     * @param chan  频道号，[0, 255]
     * @param size  每包大小，[SB_HDR_SIZE+1， MAX_BUF]
     * @param out   输出流
     */
    public SideBandOutputStream(int chan, int size, OutputStream out)
    {
        Asserts.asserts(chan >= 1 && chan <= 255, "Channel值["+chan+"]必须在[1, 255]之间");
        Asserts.asserts(size > SB_HDR_SIZE && size <= SB_MAX_BUF, "Packet大小["+size+"]必须在[" + (SB_HDR_SIZE+1) + ", " +SB_MAX_BUF + "]之间");
        
        this.out = out;
        this.buffer = new byte[size];
        this.buffer[4] = (byte)chan;
        this.cnt = SB_HDR_SIZE;
    }

    public void flushBuffer() throws IOException
    {
        if (cnt > SB_HDR_SIZE)
            writeBuffer();
    }

    @Override
    public void flush() throws IOException
    {
        flushBuffer();
        out.flush();
    }

    @Override
    public void write(final byte[] b, int off, int len) throws IOException
    {
        while (len > 0)
        {
            int capacity = buffer.length - cnt;
            if (cnt == SB_HDR_SIZE && capacity < len)
            {
                formatLength(buffer, buffer.length);
                out.write(buffer, 0, SB_HDR_SIZE);
                out.write(b, off, capacity);
                off += capacity;
                len -= capacity;
            }
            else
            {
                if (capacity == 0)
                    writeBuffer();

                int n = Math.min(len, capacity);
                System.arraycopy(b, off, buffer, cnt, n);
                cnt += n;
                off += n;
                len -= n;
            }
        }
    }

    @Override
    public void write(final int b) throws IOException
    {
        if (cnt == buffer.length)
            writeBuffer();
        buffer[cnt++] = (byte) b;
    }

    private void writeBuffer() throws IOException
    {
        formatLength(buffer, cnt);
        out.write(buffer, 0, cnt);
        cnt = SB_HDR_SIZE;
    }
    
    private static void formatLength(byte[] lenbuffer, int w)
    {
        int o = 3;
        while (o >= 0 && w != 0)
        {
            lenbuffer[o--] = _HEX_BYTE_[w & 0xf];
            w >>>= 4;
        }
        while (o >= 0)
            lenbuffer[o--] = '0';
    }
}
